package mspbots.next.ticket.core.points;

import com.fasterxml.jackson.databind.JsonNode;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import mspbots.next.ticket.core.AbstractToolsUtil;
import mspbots.next.ticket.core.GradingRequest;
import mspbots.next.ticket.core.data.rules.RulesServiceImpl;
import mspbots.next.ticket.core.grader.GradeRuleInterceptorRegistration;
import mspbots.next.ticket.core.grader.GradeRuleInterceptorRegistry;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

import java.util.List;
import java.util.stream.Collectors;


/**
 * com.mspbots.ticketai.service.BaseService
 *
 * @author Alex bob(https://github.com/vnobo)
 * @date Created by 2019/11/12
 */
@Log4j2
@Service
@RequiredArgsConstructor
public class ScoringPointAdapter extends AbstractToolsUtil {

    private final RulesServiceImpl rulesService;
    private final GradeRuleInterceptorRegistry ruleInterceptorRegistry;

    public Mono<List<GradeRuleInterceptorRegistration>> initializerRules(Long tenantId, GradingRequest request) {
        return this.rulesService.findByEnable(tenantId)
                .limitRate(10, 5)
                .map(rule -> rule.request(request))
                .publishOn(Schedulers.parallel())
                .flatMap(this.ruleInterceptorRegistry::registryInterceptor)
                .collectList();
    }

    public Mono<PointTicketRegistration> grading(List<GradeRuleInterceptorRegistration> ruleInterceptorRegistrations,
                                                 JsonNode ticket) {
        return Flux.fromStream(ruleInterceptorRegistrations.parallelStream())
                .limitRate(10, 5)
                .flatMap(gradeRuleRegistration -> gradeRuleRegistration.scoring(ticket)
                        .onErrorResume(err -> {
                            log.error("Point ticket grading rules  [{}] scoring error, msg: {}",
                                    gradeRuleRegistration.getName(), err.getMessage());
                            return Mono.empty();
                        }))
                .collectList()
                .map(pointResults -> this.setPointDetail(ticket, pointResults));
    }

    private PointTicketRegistration setPointDetail(JsonNode ticket, List<PointResult> pointResults) {
        PointTicketRegistration pointTicketRegistration = PointTicketRegistration.withEntity(ticket);
        int point = pointResults.parallelStream().mapToInt(PointResult::getPoint).sum();
        List<?> detail = pointResults.parallelStream().map(PointResult::getDetail).collect(Collectors.toList());
        return pointTicketRegistration.point(point).detail(detail);
    }

}
